package cn.jinbyte.web.interceptor;

import cn.jinbyte.web.annotation.Idempotent;
import cn.jinbyte.web.exception.IdempotentException;
import com.google.common.collect.Lists;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.NonNull;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.List;

/**
 * 幂等性拦截器抽象类
 *
 * @author jinty
 */
public abstract class IdempotentInterceptor implements HandlerInterceptor {
    List<String> includeMethods = Lists.newArrayList("POST", "PUT", "PATCH");

    /**
     * 生成幂等性Key
     *
     * @param request 请求对象
     * @return 幂等性Key
     */
    public String generateIdempotencyKey(HttpServletRequest request) throws IOException {
        String requestURI = request.getRequestURI();
        StringBuilder keyBuilder = new StringBuilder();
        keyBuilder.append(request.getQueryString() != null ? "?" + request.getQueryString() : "");
        if (includeMethods.contains(request.getMethod())) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
            String body = reader.readLine();
            keyBuilder.append(body);
        }
        return requestURI + keyBuilder.hashCode();
    }

    /**
     * 是否拦截处理
     *
     * @param request    请求对象
     * @param annotation 注解对象
     * @return 是否拦截
     */
    public abstract boolean isIdempotent(HttpServletRequest request, Idempotent annotation) throws IOException;

    @Override
    public boolean preHandle(@NonNull HttpServletRequest request,
                             @NonNull HttpServletResponse response,
                             @NonNull Object handler) throws Exception {
        // 非方法请求，不处理
        if (!(handler instanceof HandlerMethod handlerMethod)) {
            return true;
        }
        // 非指定请求方式，不处理
        String method = request.getMethod();
        if (!includeMethods.contains(method)) {
            return true;
        }

        // 没有幂等注解，不处理
        Method apiMethod = handlerMethod.getMethod();
        Idempotent annotation = apiMethod.getAnnotation(Idempotent.class);
        if (annotation == null) {
            return true;
        }
        if (this.isIdempotent(request, annotation)) {
            throw new IdempotentException(annotation.message());
        }
        return true;
    }
}